/**
* Copyright (C) 2016 Anyka(Guangzhou) Microelectronics Technology CO.,LTD.
* File Name: record_ctrl.c
* Author: Huanghaitao
* Update date: 2016-03-22
* Description:
* Notes:
* History: V2.0.0 react revision
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mount.h>

#include "record_ctrl.h"
#include "internal_time.h"

#include "ak_common.h"
#include "ak_thread.h"
#include "ak_cmd_exec.h"
#include "ak_sd_card.h"

#include "ak_vi.h"
#include "ak_venc.h"
#include "ak_ai.h"
#include "ak_aenc.h"
#ifdef CONFIG_DANA_SUPPORT
#include "ak_dana.h"
#endif
#include "ak_config.h"
#include "ak_alarm.h"
#include "ak_dvr_record.h"
#include "ak_dvr_file.h"
#include "ak_dvr_disk.h"
#include "ak_dvr_replay.h"

/*  record prefix and path */
#define RECORD_DEFAULT_PATH	"/mnt/CYC_DV/"
#define VIDEO_FILE_PREFIX	"CYC_DV_"
#define INVALID_VALUE		-1
#define SECONDS_PER_DAY		(24*60*60)

/* record plan action */
enum record_plan_action {
    RECORD_PLAN_NONE = 0X00,		//no record plan
    RECORD_PLAN_NO_CONFIG,			//record plan info not config
	RECORD_PLAN_START,				//record plan start
	RECORD_PLAN_END,				//record play end
};

/* record trigger action */
enum record_alarm_action {
    RECORD_ALARM_NONE = 0X00,		//no record alarm
	RECORD_ALARM_NO_CONFIG,			//record alarm info not config
	RECORD_ALARM_START,				//record alarm start
	RECORD_ALARM_END,				//record alarm end
};

/* send alarm mode */
enum send_alarm_mode_e {
    SEND_ALARM_MODE_ONLY_PHOTO = 0x00,      //only photo mode
    SEND_ALARM_MODE_BOTH,              		//both photo & record mode
};

/* send alarm mode */
enum alarm_sensitivity_level_e {
	ALARM_SENS_LEVEL_CLOSE = 0x00,			//close sensitivity
	ALARM_SENS_LEVEL_LOW,					//low sensitivity
	ALARM_SENS_LEVEL_MID,					//middle sensitivity
	ALARM_SENS_LEVEL_HIGH,					//high sensitivity
	ALARM_SENS_LEVEL_NUM					//level number
};

/* current plan record info */
typedef struct _plan_record {
    unsigned char status;				//plan record status
	int index;					//current started record plan's index
	int start_wday;     		//plan record start week day
    time_t start_time;     		//plan record start time
    time_t end_time;       		//plan record end time
    time_t next_time;      		//plan record next time
}plan_record_s;

/* current alarm record info */
typedef struct _alarm_record {
    unsigned char status;			//alarm record status
	unsigned int pre_time;      	//alarm record pre time, second
    unsigned int stop_time;      	//alarm record stop time, second
	time_t start_time;				//alarm detection start time
    time_t end_time;     			//end alarm time
}alarm_record_s;

struct record_ctrl_s {
    unsigned char run_flag;	        //record control thread run flag
    unsigned char except_type;	    //except type
	unsigned char detect_type;      //detection type
	unsigned char update_plan_time; //update plan time flag
	unsigned char format_card;      //format card flag
	enum dvr_file_type file_type;	//dvr file type

	void *vi_handle;				//video input handle
	void *ai_handle;				//audio input handle

	ak_pthread_t main_tid;			//read control main thread ID
	enum dvr_record_type record_mode;
	enum dvr_record_type switch_mode;
    plan_record_s plan;				//plan record info
    alarm_record_s alarm;
};

static struct record_ctrl_s record_ctrl = {0};

static void repair_sd_card_ro(void)
{
    char res[1000] = {0};

	ak_cmd_exec("mount | grep '/dev/mmcblk0p1 on /mnt'", res, sizeof(res));
	if(strlen(res) > 0) {
		ak_print_normal_ex("remount /dev/mmcblk0p1\n");
		ak_cmd_exec("mount -o remount,rw /dev/mmcblk0p1", NULL, 0);
		return;
	}

	memset(res, 0x00, sizeof(res));

	ak_cmd_exec("mount | grep '/dev/mmcblk0 on /mnt'", res, sizeof(res));
	if(strlen(res) > 0) {
		ak_print_normal_ex("remount /dev/mmcblk0\n");
		ak_cmd_exec("mount -o remount,rw /dev/mmcblk0", NULL, 0);
		return;
	}
}

static void get_video_record_error(int error_code, const char *error_desc)
{
	ak_print_normal_ex("--- error_code=%d ---\n", error_code);
	switch(error_code){
    case DVR_EXCEPT_NO_VIDEO://can't capture video data
    	break;
    case DVR_EXCEPT_NO_AUDIO://can't capture audio data
    	break;
    case DVR_EXCEPT_MUX_SYSERR:
    	ak_print_warning_ex("--- mux system error ---\n");
    	break;
    case DVR_EXCEPT_SD_NO_SPACE://SD card space not enough
    	ak_print_warning_ex("--- SD card no more space ---\n");
    	break;
    case DVR_EXCEPT_SD_RO://SD card read only
    	ak_print_warning_ex("--- SD card read only, we'll repaire it ---\n");
    	break;
    default:
    	break;
    }

    record_ctrl.except_type |= error_code;
    ak_print_normal_ex("--- except_type=%d ---\n", record_ctrl.except_type);
}

static int report_exception(int except_type)
{
	unsigned char tmp_type = DVR_EXCEPT_NONE;

    if (record_ctrl.except_type & DVR_EXCEPT_SD_REMOVED) {
    	tmp_type |= DVR_EXCEPT_SD_REMOVED;
    }
    if (record_ctrl.except_type & DVR_EXCEPT_SD_UMOUNT) {
    	tmp_type |= DVR_EXCEPT_SD_UMOUNT;
    }

    /* we clear current exception type after report */
    record_ctrl.except_type = tmp_type;

    return AK_SUCCESS;
}

/**
 * get_latest_file - get latest file according to time sort
 * @file_name[OUT]:
 * return: 0 success, -1 failed
 * notes:
 */
static int get_latest_file(char *file_name, int name_len)
{
	char cmd[256] = {0};
	struct video_record_config *record_config = ak_config_get_record();

	sprintf(cmd, "ls -t %s%s* > /tmp/record.list",
		record_config->path, VIDEO_FILE_PREFIX);
	system(cmd);

	FILE *fp_list = fopen("/tmp/record.list", "r");
	if(!fp_list){
		ak_print_error_ex("fopen /tmp/record.list failed, %s\n", strerror(errno));
		return AK_FAILED;
	}

	int ret = AK_FAILED;

	if(fgets(file_name, name_len, fp_list)){
		int name_len = strlen(file_name);
		file_name[name_len-1] = 0x00;
		ak_print_normal_ex("name_len=%d, file_name: %s\n", name_len, file_name);
		ret = AK_SUCCESS;
	}
	fclose(fp_list);
	remove("/tmp/record.list");

	return ret;
}

static void check_sd_ro(void)
{
	char file_name[255] = {0};

	switch(ak_dvr_check_sd_ro()) {
	case AK_TRUE:
		ak_print_normal_ex("The SD Card is Read Only, repair it\n");
		/* get the last modify file name */
		if (!get_latest_file(file_name, sizeof(file_name))) {
			ak_print_normal_ex("The badfile in sd card is: %s\n", file_name);
			if (ak_dvr_repair_ro_disk(file_name)) {
				ak_print_error_ex("The SD Card repaired failed\n");
			} else {
				ak_print_notice_ex("The SD Card repaired OK\n");
			}
		}

		ak_dvr_file_delete_tmp();
		break;
	case AK_FALSE:
		ak_print_normal_ex("The SD Card is OK\n");
		break;
	default:
		ak_print_normal_ex("check SD read only failed\n");
		break;
	}
}

/**
 * stop_record: stop the current record
 * @void:
 * return: none
 * notes: stop condition:
 *		1. any kind of exceptions, ex.: DVR_EXCEPT_SD_NO_SPACE
 *		2. plan record time is out
 */
static void stop_record(void)
{
	ak_print_normal_ex("except_type=0x%X\n", record_ctrl.except_type);
	ak_dvr_record_stop();
    record_ctrl.plan.status = RECORD_PLAN_NONE;
	record_ctrl.alarm.status = RECORD_ALARM_END;

    if (DVR_EXCEPT_SD_RO & record_ctrl.except_type) {
    	repair_sd_card_ro();
    }

    if ((DVR_EXCEPT_MUX_SYSERR & record_ctrl.except_type)
    	|| (DVR_EXCEPT_SD_RO & record_ctrl.except_type)) {
    	/* check sdcard readonly again, if still RO, we will repair it. */
		check_sd_ro();
    }
}

/**
 * init_record_common - init record common part.
 * @file_type: record file type
 * @interval: audio frame interval
 * return: void
 */
static void init_record_common(enum dvr_file_type file_type, int interval)
{
	/* video record param init */
	struct record_param record;
	struct video_record_config *record_config = ak_config_get_record();
	struct camera_disp_config *camera = ak_config_get_camera_info();
	struct video_config *video = ak_config_get_sys_video();
	struct dvr_file_param file_param = {0};

	record_ctrl.file_type = file_type;
	record.vi_handle = record_ctrl.vi_handle;
	record.ai_handle = record_ctrl.ai_handle;

	record.width = camera->main_width;
	record.height = camera->main_height;
	if (record.width <= 640) {
		record.enc_chn = ENCODE_SUB_CHN;
	} else {
		record.enc_chn = ENCODE_MAIN_CHN;
	}

	record.file_fps = record_config->save_file_fps;
	record.file_bps = record_config->save_file_kbps;
	record.gop_len = video->gop_len;

	record.sample_rate = record_config->sample_rate;
	record.frame_interval = interval;

	record.cyc_flag = record_config->save_cyc_flag;
	record.file_type = file_type;
    record.duration = (record_config->duration * 1000);
	strcpy(record.rec_prefix, VIDEO_FILE_PREFIX);
	if (strlen(record_config->path)) {
		strcpy(record.rec_path, record_config->path);
	} else {
		strcpy(record.rec_path, RECORD_DEFAULT_PATH);
	}
	record.error_report = get_video_record_error;

    ak_dvr_record_init(&record);
	file_param.cyc_flag = record.cyc_flag;
    file_param.type = record.file_type;
    strcpy(file_param.rec_prefix, record.rec_prefix);
   	strcpy(file_param.rec_path, record.rec_path);
	ak_dvr_replay_init(&file_param);

}

/**
 * check_plan_continuity - get record plan's next start time in plan table,
 *		and check the plan's continuity.
 * @plan_index[IN]: current started record plan's wday
 * @cur_wday[IN]: current started record plan's wday
 * return: 0 continuity; -1 interrupted
 * notes: we can use this to check the record plan's continuity
 */
static int check_plan_continuity(int plan_index, int cur_wday)
{
	if((plan_index >= MAX_RECORD_PLAN_NUM) || (plan_index < 0)){
		return AK_FAILED;
	}
	if((cur_wday >= DAY_PER_WEEK) || (cur_wday < 0)){
		return AK_FAILED;
	}

	struct video_record_config *record = ak_config_get_record();
    if(NULL == record){
		return AK_FAILED;
    }

	int i = 0;
	int ret = AK_FAILED;
	int diff_day = 0;
	int next_wday = 0;
    time_t next_time = 0;
    time_t cur_time = 0;

	for(i=cur_wday; i<DAY_PER_WEEK; ++i){
		next_wday = i + 1;
		++diff_day;
		if(DAY_PER_WEEK == next_wday){
			next_wday = 0;
		}

		ak_print_normal("\nrecord plan[%d], cur week_enalbe[%d]=%d, "
	    	"next week_enalbe[%d]=%d\n",
			plan_index, i, record->plan[plan_index].week_enalbe[i],
			next_wday, record->plan[plan_index].week_enalbe[next_wday]);

		if(record->plan[plan_index].active
	    	&& record->plan[plan_index].week_enalbe[next_wday]){
	    	cur_time = get_passed_seconds();
			next_time = (SECONDS_PER_DAY *diff_day)
				+ day_secs_to_total(record->plan[plan_index].start_time);

			ak_print_normal("\n\t cur_secs:\t %ld(s), %s\n",
				cur_time, ak_seconds_to_string(cur_time));
			ak_print_normal("\t plan_end_time:\t %ld(s), %s\n",
				record_ctrl.plan.end_time,
				ak_seconds_to_string(record_ctrl.plan.end_time));
			ak_print_normal("\t plan next_time:\t %ld(s), %s\n",
				next_time, ak_seconds_to_string(next_time));
			if((record_ctrl.plan.end_time + 1) >= next_time){
				/* update current record plan info */
				record_ctrl.plan.end_time = (SECONDS_PER_DAY *diff_day)
					+ day_secs_to_total(record->plan[plan_index].end_time);
				record_ctrl.plan.start_wday = next_wday;
				ret = AK_SUCCESS;
			}
			break;
	    }
	}

	return ret;
}

static void check_plan_record_start(void)
{
	struct video_record_config *record = ak_config_get_record();
    if(NULL == record){
    	record_ctrl.plan.status = RECORD_PLAN_NO_CONFIG;
		return;
    }

	int i = 0;
    struct tm *cur = NULL;
    time_t start = 0;
    time_t end = 0;
    time_t cur_secs = 0;

	for(i=0; i<MAX_RECORD_PLAN_NUM; ++i){
    	cur = get_tm_time();
#if 0
        ak_print_normal("\n\t record plan[%d], active=%d, week_enalbe[%d]=%d, "
        	"start_time: %ld, end_time: %ld\n",
			i, record->plan[i].active,
			cur->tm_wday, record->plan[i].week_enalbe[cur->tm_wday],
			record->plan[i].start_time, record->plan[i].end_time);
#endif

        if(record->plan[i].active && record->plan[i].week_enalbe[cur->tm_wday]){
			cur_secs = get_passed_seconds();
			start = day_secs_to_total(record->plan[i].start_time);
			end = day_secs_to_total(record->plan[i].end_time);
			ak_print_normal("\t\t config->plan[%d].start_time:\t %ld(s)\n",
				i, record->plan[i].start_time);
			ak_print_normal("\t\t config->plan[%d].end_time:\t %ld(s)\n",
				i, record->plan[i].end_time);
			ak_print_normal("\t\t cur_time:\t %ld(s), %s\n",
				cur_secs, ak_seconds_to_string(cur_secs));
			ak_print_normal("\t\t start_time:\t %ld(s), %s\n",
				start, ak_seconds_to_string(start));
			ak_print_normal("\t\t end_time:\t %ld(s), %s\n",
				end, ak_seconds_to_string(end));
        	if((cur_secs >= start) && (cur_secs < end)){
            	record_ctrl.plan.index = i;
            	record_ctrl.plan.start_wday = cur->tm_wday;
            	record_ctrl.plan.end_time = end;
	            record_ctrl.plan.status = RECORD_PLAN_START;
	            ak_dvr_record_start(RECORD_TRIGGER_TYPE_PLAN);

	            cur_secs = get_passed_seconds();
	            ak_print_normal("\n\t we will start the record plan[%d]\n", i);
	            ak_print_normal("\t cur_time:\t %ld(s), %s\n",
					cur_secs, ak_seconds_to_string(cur_secs));
				ak_print_normal("\t start_time:\t %ld(s), %s\n",
					start, ak_seconds_to_string(start));
				ak_print_normal("\t end_time:\t %ld(s), %s\n\n",
					end, ak_seconds_to_string(end));
				break;
            }
        }
    }
}

static void check_plan_record_stop(void)
{
    time_t cur_secs = get_passed_seconds();

	if(cur_secs >= record_ctrl.plan.end_time){
    	/* get next record plan start time */
    	if(check_plan_continuity(record_ctrl.plan.index,
    		record_ctrl.plan.start_wday) < 0){
    		ak_print_normal_ex("\n\t we will stop the record plan[%d]\n",
    			record_ctrl.plan.index);
			ak_print_normal_ex("\t cur_time: %s\n", ak_seconds_to_string(cur_secs));
			ak_print_normal_ex("\t end_time: %s\n",
				ak_seconds_to_string(record_ctrl.plan.end_time));
	        record_ctrl.plan.status = RECORD_PLAN_END;
	        stop_record();
    	}
    }
}


static int check_record_exception(void)
{
	if(SD_STATUS_CARD_INSERT & ak_sd_check_insert_status()){
		if((record_ctrl.except_type & DVR_EXCEPT_SD_REMOVED)){
			ak_print_normal_ex("--- SD Card Inserted ---\n");
		}
		record_ctrl.except_type &= (~DVR_EXCEPT_SD_REMOVED);
		if(AK_SUCCESS == ak_sd_check_mount_status()){
	    	if(record_ctrl.except_type & DVR_EXCEPT_SD_UMOUNT){
				ak_print_normal_ex("--- SD Card mounted ---\n");
				/* check sdcard readonly or not, and repair it. */
				check_sd_ro();

				/* sd card may reinsert */
				ak_dvr_create_list();
			}
	        record_ctrl.except_type &= (~DVR_EXCEPT_SD_UMOUNT);
	    }else{
	    	record_ctrl.except_type |= DVR_EXCEPT_SD_UMOUNT;
	    }
	}else{
		if(0 == (record_ctrl.except_type & DVR_EXCEPT_SD_REMOVED)){
			ak_print_normal_ex("--- SD Card has removed ---\n\n");
		}
		record_ctrl.except_type |= DVR_EXCEPT_SD_REMOVED;
		record_ctrl.except_type |= DVR_EXCEPT_SD_UMOUNT;
	}

    return record_ctrl.except_type;
}

/* check_record_plan - check and change record plan status */
static int check_record_plan(void)
{
    switch(record_ctrl.plan.status){
    case RECORD_PLAN_NONE:
    case RECORD_PLAN_END:
    	check_plan_record_start();

        break;
    case RECORD_PLAN_START:
    	check_plan_record_stop();

        break;
    default:
        record_ctrl.plan.status = RECORD_PLAN_NONE;
        break;
    }

    return record_ctrl.plan.status;
}

static void check_alarm_record_start(void)
{
    time_t end = 0;
    time_t cur_secs = 0;
	int alarm_time = ak_alarm_get_time();
	static int alarm_time_bak = 0;

	if(0 == alarm_time) {
		return;
	}

	/* calendar time switch time back; alarm trigger time have been record. */
	if((alarm_time < alarm_time_bak)
		|| ((alarm_time < record_ctrl.alarm.end_time)
			&& (alarm_time_bak != alarm_time))) {
		ak_print_normal_ex("alarm_time_bak:%d(s), alarm_time:%d(s)\n",
			alarm_time_bak, alarm_time);
		alarm_time_bak = alarm_time;
	}

	if(alarm_time != alarm_time_bak) {
    	alarm_time_bak =alarm_time;
		cur_secs = get_passed_seconds();
		end = cur_secs + record_ctrl.alarm.stop_time;

		ak_print_normal_ex("alarm.start_time: %ld(s), %s\n",
			cur_secs, ak_seconds_to_string(cur_secs));
		ak_print_normal_ex("alarm.end_time: %ld(s), %s\n",
			end, ak_seconds_to_string(end));
    	record_ctrl.alarm.start_time = cur_secs;
    	record_ctrl.alarm.end_time = end;
        record_ctrl.alarm.status = RECORD_ALARM_START;
    	ak_dvr_record_start(RECORD_TRIGGER_TYPE_ALARM);
    }
}

static void check_alarm_record_stop(void)
{
    time_t cur_secs = get_passed_seconds();
    int alarm_time = ak_alarm_get_time();

	if(0 != alarm_time) {
		record_ctrl.alarm.end_time = cur_secs + record_ctrl.alarm.stop_time;
	}
	if(cur_secs >= record_ctrl.alarm.end_time){
		ak_print_normal_ex("alarm.start_time: %s\n",
			ak_seconds_to_string(record_ctrl.alarm.start_time));
		ak_print_normal_ex("alarm.end_time: %s\n",
			ak_seconds_to_string(record_ctrl.alarm.end_time));

        stop_record();
    	record_ctrl.alarm.status = RECORD_ALARM_END;
    }
}

/* check_record_alarm - check and change record alarm status */
static int check_record_alarm(void)
{
    switch(record_ctrl.alarm.status){
    case RECORD_ALARM_NONE:
    case RECORD_ALARM_END:
    	check_alarm_record_start();
        break;
    case RECORD_ALARM_START:
    	check_alarm_record_stop();
        break;
    default:
        record_ctrl.alarm.status = RECORD_ALARM_NONE;
        break;
    }

    return record_ctrl.alarm.status;
}

/**
 * change_mode - change record to new mode.
 * @void:
 * return: 0, success; -1, failed
 */
static int change_mode(void)
{
	/* change record mode */
	if((RECORD_PLAN_START == record_ctrl.plan.status)
		|| (RECORD_ALARM_START == record_ctrl.alarm.status)) {
		stop_record();
	}

	record_ctrl.record_mode = record_ctrl.switch_mode;
	switch(record_ctrl.record_mode){
	case RECORD_TRIGGER_TYPE_ALARM:
		ak_dvr_record_turn_on_trigger(record_ctrl.alarm.pre_time);
		break;
	case RECORD_TRIGGER_TYPE_PLAN:
		ak_dvr_record_turn_off_trigger();
		break;
	case RECORD_TRIGGER_TYPE_RESERVED:
		break;
	default:
		break;
	}

	return AK_SUCCESS;
}

static int format_card(void)
{
	int umount_ok = 0;
    int ret = AK_FAILED;
	char result[10] = {0};

	if((RECORD_PLAN_START == record_ctrl.plan.status)
		|| (RECORD_ALARM_START == record_ctrl.alarm.status)) {
		stop_record();
	}
	/* do umount */
	if(umount("/mnt/")) {
		ak_cmd_exec("ls /proc/`pidof anyka_ipc`/fd -l", NULL, 0);
		ak_print_notice_ex("umount card fail!\n");
		ak_sleep_ms(400);
		/* do umount */
		if(umount("/mnt/")) {
			ak_cmd_exec("ls /proc/`pidof anyka_ipc`/fd -l", NULL, 0);
			ak_print_notice_ex("umount card fail!\n");
		}
		else
			umount_ok = 1;
	}else{
		umount_ok = 1;
	}
	if(umount_ok ){
		ak_print_notice_ex("format card start!\n");
		/* use result, let command finisih and return */
		ak_cmd_exec("mkfs.vfat /dev/mmcblk0p1", result, sizeof(result));
		ak_cmd_exec("mount /dev/mmcblk0p1 /mnt/", NULL, 0);
		ak_print_notice_ex("format card finished!\n");
		ret = AK_SUCCESS;
	}

	return ret;
}
/** record_ctrl_main
 * @arg[IN]: thread input arg
 * return: none
 * notes:
 */
static void* record_ctrl_main(void *arg)
{
    ak_print_normal_ex("thread id: %ld\n", ak_thread_get_tid());
	ak_thread_set_name("record_ctrl");

    while(record_ctrl.run_flag){
        check_record_exception();
		if(DVR_EXCEPT_NONE == record_ctrl.except_type){
			switch(record_ctrl.record_mode) {
			case RECORD_TRIGGER_TYPE_ALARM:
				check_record_alarm();
				break;
			case RECORD_TRIGGER_TYPE_PLAN:
				check_record_plan();
				break;
			default:
				break;
			}
		}else{
			/* we have aleady started record, then stop it */
			if((RECORD_PLAN_START == record_ctrl.plan.status)
				|| (RECORD_ALARM_START == record_ctrl.alarm.status)){
				stop_record();
			}

			report_exception(record_ctrl.except_type);
		}

	    ak_sleep_ms(300);
		if (record_ctrl.format_card) {
			format_card();
			record_ctrl.format_card = 0;
		}
		if (record_ctrl.update_plan_time) {
			if ((RECORD_TRIGGER_TYPE_PLAN == record_ctrl.record_mode)
				&& (RECORD_PLAN_START == record_ctrl.plan.status)) {
				stop_record();
			}
			record_ctrl.update_plan_time = 0;
		}
	    if (record_ctrl.record_mode != record_ctrl.switch_mode) {
	    	change_mode();
	    }
	}

	ak_print_normal_ex("### thread id: %ld exit ###\n", ak_thread_get_tid());

	ak_thread_exit();
	return NULL;
}

/**
 * record_ctrl_update_dvr_plan_time - tell dvr, dvr plan time have been update.
 * @ void:
 * return: 0, success; -1, failed
 */
int record_ctrl_update_dvr_plan_time(void)
{
	ak_print_normal_ex("\n");
	record_ctrl.update_plan_time = 1;

	return AK_SUCCESS;
}

/**
 * record_ctrl_request_format_card - tell dvr to do format card.
 * @ void:
 * return: 0, success; -1, failed
 */
int record_ctrl_request_format_card(void)
{
	ak_print_normal_ex("\n");
	record_ctrl.format_card = 1;

	return AK_SUCCESS;
}

/**
 * record_ctrl_set_mode - set record control mode.
 * @new_mode[IN]:  new record control mode to be set
 * return: 0, success; -1, failed
 */
int record_ctrl_set_mode(enum dvr_record_type new_mode)
{
	if ((new_mode == record_ctrl.record_mode)
		|| (new_mode < RECORD_TRIGGER_TYPE_RESERVED)
		|| (new_mode > RECORD_TRIGGER_TYPE_ALARM)) {
		ak_print_error_ex("argument fail. new_mode: %d\n", new_mode);
		return AK_FAILED;
	}

	ak_print_notice_ex("record new_mode: %d\n", new_mode);
	record_ctrl.switch_mode = new_mode;

	return AK_SUCCESS;
}

/**
 * record_ctrl_init - init record control env and other sub-modules.
 *      start record control thread.
 * @vi_handle[IN]: success opened video input handle
 * @ai_handle[IN]: success opened audio input handle
 * @file_type[IN]: record file type
 * return: none
 */
void record_ctrl_init(void *vi_handle, void *ai_handle,
					enum dvr_file_type file_type)
{
	if(record_ctrl.run_flag){
		ak_print_normal_ex("already inited...\n");
		return;
	}

    struct video_record_config *record_config = ak_config_get_record();
	ak_print_normal("\n---------------------------------------------------------"
		   "-----------------------\n");
	ak_print_normal("\t entering record_ctrl_init\n");
	ak_print_normal("\t %s, record_mode: %s\n",
		get_readable_time_string(),
		(RECORD_TRIGGER_TYPE_PLAN == record_config->record_mode ?
			"plan record" : "alarm record"));

    int interval = 0;
    switch(file_type) {
    case DVR_FILE_TYPE_MP4:
    	interval = AMR_FRAME_INTERVAL;
    	ak_print_notice_ex("^^^^^ record file type is mp4 ^^^^^\n");
    	break;
    case DVR_FILE_TYPE_AVI:
    	interval = AUDIO_DEFAULT_INTERVAL;
    	ak_print_notice_ex("^^^^^ record file type is avi ^^^^^\n");
    	break;
    default:
    	ak_print_error_ex("^^^^^ unsupport record file_type=%d ^^^^^\n", file_type);
    	return;
    }

	if (AK_SUCCESS == ak_ai_set_frame_interval(ai_handle, interval)) {
		ak_print_normal("ai frame interval=%d\n", interval);
	}
	ak_ai_start_capture(ai_handle);

	record_ctrl.vi_handle = vi_handle;
	record_ctrl.ai_handle = ai_handle;
	init_record_common(file_type, interval);

	record_ctrl.record_mode = record_config->record_mode;
	record_ctrl.switch_mode = record_ctrl.record_mode;
    record_ctrl.run_flag = AK_TRUE;
    record_ctrl.except_type = (DVR_EXCEPT_SD_REMOVED | DVR_EXCEPT_SD_UMOUNT);
	record_ctrl.detect_type = SYS_DETECT_TYPE_NUM;
    record_ctrl.update_plan_time = 0;
	record_ctrl.format_card = 0;
	/* alarm record info */
	record_ctrl.alarm.status = RECORD_ALARM_NONE;
	record_ctrl.alarm.start_time = 0;
    record_ctrl.alarm.end_time = 0;
	record_ctrl.alarm.pre_time= record_config->alarm_pre_time;
   	record_ctrl.alarm.stop_time = record_config->alarm_stop_time;
   	if(RECORD_TRIGGER_TYPE_ALARM == record_ctrl.record_mode){
		ak_dvr_record_turn_on_trigger(record_ctrl.alarm.pre_time);
	}

	ak_print_normal_ex("alarm.pre_time=%d, alarm.stop_time=%d\n",
		record_ctrl.alarm.pre_time, record_ctrl.alarm.stop_time);

	/* plan record info */
    record_ctrl.plan.status = RECORD_PLAN_NONE;
    record_ctrl.plan.index = INVALID_VALUE;
	record_ctrl.plan.start_wday = INVALID_VALUE;
    record_ctrl.plan.start_time = 0;
    record_ctrl.plan.end_time = 0;
    record_ctrl.plan.next_time = 0;

    int ret = ak_thread_create(&record_ctrl.main_tid, record_ctrl_main,
        NULL, ANYKA_THREAD_MIN_STACK_SIZE, -1);
	if(AK_FAILED == ret){
		record_ctrl.run_flag = AK_FALSE;
		ak_print_normal_ex("create record_ctrl_main thread failed, ret = %d!\n",
			ret);
	}
}
//add save record
void save_record_reboot(void){
	stop_record();
}
/** record_ctrl_exit: clear record control env and other sub-modules.
 *      stop record control thread and release resource.
 * @void
 * return: none
 */
void record_ctrl_exit(void)
{
	ak_print_normal("\n\t***** enter %s *****\n", __func__);
	if(record_ctrl.run_flag){
		ak_dvr_record_exit();
		ak_dvr_replay_exit();
	    record_ctrl.run_flag = AK_FALSE;
		ak_print_notice_ex("join record control main thread\n");
		ak_thread_join(record_ctrl.main_tid);
		ak_print_notice_ex("record control main thread join OK\n");
	    ak_print_normal_ex("record ctrl has stopped!\n");
	}
	ak_print_normal("\t ***** %s OK *****\n\n", __func__);
}
